#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> //sleep();

#include "list.h"
#include "listfifostack.h"

int printInt(void *data)
{
    printf("%ld ", (unsigned long) data);
    return 0;
}

int printStr(void *data)
{
    printf("%s ", (char *) data);
    return 0;
}

void *copyInt(void **dst, const void *src)
{
    *dst = (void *)src;
    return *dst;
}

void *copyStr(void **dst, const void *src)
{
    if(src == NULL)
        return NULL;
    *dst = malloc(strlen(src) + 1);
    return (void *)strcpy((char *) *dst, (char *)src);

}

int freeInt(void *data)
{
    //do nothing
    return 0;
}

int freeStr(void *data)
{
    free(data);
    return 0;
}

int cmpStr(const void *larger, const void *smaller)
{
    return strcmp((const char *)larger, (const char *)smaller);
}

int data_int[] = {0, 1, 2, 3, 4, 5, 6, 7};
const char *data_str[] = {"abc0",
    "abc1",
    "abc2",
    "abc3",
    "abc4",
    "abc2"};

static data_op_t *opt = NULL;//= (data_op_t *)malloc(sizeof(data_op_t));
int list_index1, list_index2;

void test_list_with(list_t **pplist, BOOLEAN WILLDEL, BOOLEAN SPLIT, BOOLEAN SWAP)
{
    list_t *list = *pplist;

    int count = 0;
    int result = 0;

    list_t *lists[2] = {NULL, NULL};
    int split_at = 0;   //-2:empty, -1:first, 0:middle, 1: last

    printf("====================================================== Test List DEL= %s======================================================\n", WILLDEL? "true" : "false");

    list_for_each(list, printStr);
    for(count = 0; count < sizeof(data_str)/sizeof(data_str[0]); count ++) {
        list_add_first(list, data_str[count], NULL);
        list_add_last(list, data_str[count], NULL);
        printf("first:%s\t", (const char *)list_get_first(list));
        printf("last:%s\t", (const char *)list_get_last(list));
        printf("length: %d\t", list_size(list));
        list_for_each(list, NULL);
        puts("\n");
    }

    if(SWAP) {
        while(TRUE) {
            printf("enter swaped indexs:");
            scanf("%d %d", &list_index1, &list_index2);
            result = list_swap(list, list_index1, list_index2);
            printf("result = %d, list now after swap:", result);
            list_for_each(list, NULL);
            puts("\n");
            sleep(1);
        }
    }

    int i = 0;
    if (SPLIT) {
        switch (split_at){
            case -1:
                split_at = 0;
                break;
            case 0:
                split_at = sizeof(data_str)/sizeof(data_str[0])/2;
                break;
            case 1:
                split_at = list_size(list);
                break;
            default:
                break;
        }
        list_split(pplist, lists, split_at);
        puts("\n----loop lists[0]-----\n");
        list_for_each(lists[0], NULL);

        puts("\n----loop lists[1]-----\n");
        list_for_each(lists[1], NULL);
        
        i = 0;
        while (i < 2){
            list_destroy(&(lists[i]), NULL);
            i ++;
        }
        
        puts("\n");
    }

    puts("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");

    int old = result = 0;

    if(!SPLIT){
        for(count = 0; count < sizeof(data_str)/sizeof(data_str[0]) ; count ++) {
            if (WILLDEL){
                printf("first:%s\t", (const char *)list_get_first(list));
                printf("last:%s\t", (const char *)list_get_last(list));
                printf("length: %d\t", list_size(list));
                list_for_each(list, NULL);
            }
            //list_del_first(list, freeStr);
            //list_del_last(list, freeStr);
            old += result;
            result = 0;
            while((result += 2) < old ){
                putchar('\t');
            }

            if(WILLDEL){
                printf("_____delete :%s, result:%d", data_str[count], result = list_del_as(list, data_str[count], NULL, NULL));
            }
            puts("\n");
        }
    }
}
void test_list()
{
    list_t *list = list_new(opt);
    test_list_with(&list, FALSE, FALSE, FALSE);

    list_t *cplist = list_new_with_copy(list, NULL);

    cplist = list_join(cplist, list, NULL, NULL);

    //test qsort
    list_qsort(cplist, NULL);
    list_for_each(cplist, NULL);
    puts("\n");

    //test reverse
    list_reverse(cplist);
    list_for_each(cplist, NULL);

    list = list_destroy(&list, NULL);

    test_list_with(&cplist, TRUE, TRUE, FALSE);

    if(cplist != NULL){
        list_destroy(&cplist, NULL);
    }
    return;
}

void test_fifo()
{
    printf("====================================================== Test FIfo ===========================================================\n");
    int count = 0;
    int count1 = 0;
    int result1 = 0;
    list_fifo_t *fifo = NULL;
    printf("list_fifo created: %lx\tlength:%d\tinfo:%s\n", (long)fifo, list_fifo_size(fifo), list_fifo_get_last_error_string(fifo));


    fifo = list_fifo_new(opt);
    printf("list_fifo created: %lx\tlength:%d\tinfo:%s\n", (long)fifo, list_fifo_size(fifo), list_fifo_get_last_error_string(fifo));

    // test push & front()
    for(count = 0; count < sizeof(data_str)/sizeof(data_str[0]); count ++) {
        list_fifo_push(fifo, data_str[count], copyStr);
        printf("%s pushed, now has %d elements,  currently front: %s\tinfo:%s\n", 
                data_str[count], list_fifo_size(fifo), (char *)list_fifo_front(fifo), list_fifo_get_last_error_string(fifo));
    }

    puts("\n\n");
    char *buf_str = NULL;

    for(count = 0; count < sizeof(data_str)/sizeof(data_str[0]); count ++) {
        list_fifo_pop(fifo,(void **) (&buf_str), copyStr, freeStr);
        printf("%s poped, now has %d elements,  currently front: %s\tinfo:%s\n", 
                buf_str,
                list_fifo_size(fifo), (char *)list_fifo_front(fifo), list_fifo_get_last_error_string(fifo));
        freeStr(buf_str);

        for(count1 = 0; count1 < sizeof(data_str)/sizeof(data_str[0]); count1 ++) {
            result1 = list_fifo_contains(fifo, data_str[count1], NULL);
            //printf("does fifo contains %s? :%s, %d times\n", data_str[count1], result1>0?"true":"false", result1);
        }
    }

    printf("begin destroying fifo......\n");
    list_fifo_destroy(&fifo, freeStr);
    printf("list_fifo destroyed: %lx\n", (long)fifo);

}

void test_stack()
{
    printf("====================================================== Test Stack===========================================================\n");
    int count = 0;
    int count1 = 0;
    int result1 = 0;
    list_stack_t *stack = NULL;
    printf("list_stack created: %lx\tlength:%d\tinfo:%s\n", (long)stack, list_stack_size(stack), list_stack_get_last_error_string(stack));


    stack = list_stack_new(opt);
    printf("list_stack created: %lx\tlength:%d\tinfo:%s\n", (long)stack, list_stack_size(stack), list_stack_get_last_error_string(stack));

    // test push & top()
    for(count = 0; count < sizeof(data_str)/sizeof(data_str[0]); count ++) {
        list_stack_push(stack, data_str[count], copyStr);
        printf("%s pushed, now has %d elements,  currently top: %s\tinfo:%s\n", 
                data_str[count], list_stack_size(stack), (char *)list_stack_top(stack), list_stack_get_last_error_string(stack));
    }

    puts("\n\n");
    char *buf_str = NULL;

    for(count = 0; count < sizeof(data_str)/sizeof(data_str[0]); count ++) {
        list_stack_pop(stack,(void **) (&buf_str), copyStr, freeStr);
        printf("%s poped, now has %d elements,  currently top: %s\tinfo:%s\n", 
                buf_str,
                list_stack_size(stack), (char *)list_stack_top(stack), list_stack_get_last_error_string(stack));
        freeStr(buf_str);

        for(count1 = 0; count1 < sizeof(data_str)/sizeof(data_str[0]); count1 ++) {
            result1 = list_stack_contains(stack, data_str[count1], NULL);
            //printf("does stack contains %s? :%s, %d times\n", data_str[count1], result1>0?"true":"false", result1);
        }
    }

    printf("begin destroying stack......\n");
    list_stack_destroy(&stack, freeStr);
    printf("list_stack destroyed: %lx\n", (long)stack);


}

int cmd_arg_ids[] = {0, 1, 2, 3};

char *cmd_args[] = {"all", "list", "fifo", "stack"};

int check_cmd_arg(int argc, char *argv[])
{
    if(1 == argc ){
        return 0;
    }
    
    int count;
    for(count = 0; count < sizeof(cmd_args)/sizeof(cmd_args[0]); count ++){
        if(strcmp(argv[1], cmd_args[count]) == 0){
            return count;
        }
    }

    return -1;
}

void test_all()
{
    test_list();
    test_fifo();
    test_stack();
}

int main(int argc, char *argv[])
{
    opt = (data_op_t *)malloc(sizeof(data_op_t));
    opt->copy = copyStr;
    opt->free = freeStr;
    opt->cmp = cmpStr;
    opt->foreach = printStr;

    int check_what = check_cmd_arg(argc, argv);
    list_index1 = list_index2 = 0;

    int count = 0;
    while(count++ < 100){
        switch(check_what){
            case -1:
                return -1;
            case 1:
                test_list();
                break;
            case 2:
                test_fifo();
                break;
            case 3:
                test_stack();
                break;
            case 0:
                test_all();
                break;
            default:
                break;
        }

        printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> %d times\n", count);
        sleep(1);
    }

    free(opt);
    return 0;
}
